/**
 *@fileOverview This script aids in the creation of controllers that modify
 *a layer property's value based upon the first and second keyframe values.
 *
 *@author Collin Brooks collin.brooks@gmail.com
 *@version 1
 */

/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true,
plusplus: true, bitwise: true, regexp: true, newcap: true,
immed: true, maxlen: 80 */

//This script utilizes and extends the COB object. If the COB object is not
//defined, define it.

//Import the COB include

//TODO:
//Make font color darker.
//Search for duplicate controllers
//Check if property is a mask.
//Write help documentation
//Resize Help button
//Button that gets the currently selected layer and auto fills the information
    //based upon that selection
//JS Lint

COB = undefined; //Comment out for production
$.evalFile(new File(new File($.fileName).path + "/COB_v1.jsxinc"));
//#include "./COB_v1.jsxinc" //Comment out for dev mode.
$.localize = true;
COB.debug = 1;
COB.LOG.debug("Beginning CC Object Init.");
/**
 * The Controller Creator Object
 * This object is the main namespace for the COB Controller Creator and
 * contains all functions and properties that the Polaroid Maker requires in
 * order to function. It is placed within the COB object in order to preserve
 * the global namespace.
 * @constructor
 * @namespace
 */
COB.CC = {};

/**
 *The name of this script
 *@type String
 */
COB.CC.scriptName = "COB - Controller Creator";

/**
 *The string representation of the version of this script
 *@type String
 */
COB.CC.scriptVersion = "v1";

/**
 *The file name of this script
 *@type String
 */
COB.CC.scriptFileName = "COB_Controller_Creator.jsx";

/**
 *The description of this script
 *@type String
 */
COB.CC.scriptDescription = "This script aids in the creation of " +
    "Slider Expression Effects for selected properties. These controllers " +
    "modify the value of the property based upon the properties 1st and 2nd " +
    "keyframes.";

/**
 *A list of errors for the CC script
 */
COB.CC.error = {
    ACTIVE_ITEM_NOT_COMP: "The current active item must be a composition " +
        "but it isn't.",
    ACTIVE_ITEM_NOT_FOUND: "An active item could not be found.",
    UNABLE_TO_SET_RECEIVING_COMP: "The receiving Composition could not be " +
        "set within the receiving composition list",
    UNABLE_TO_SET_CONTROLLER: "The controller could not be " +
        "set within the controller list",
    NEED_TO_SELECT_RECEIVING_COMP: "Please select a receiving comp before " +
        "performing this action.",
    NO_SELECTED_PROPERTIES: "There aren't any properties selected within the " +
        "provided composition"
};

/**
 *The current composition that is active
 *@type CompItem
 */
COB.CC.currentComp = {};

/**
 *An array of currently selected layers within the active composition
 *@type {LayerItems[]}
 */
COB.CC.selectedProperties = {};

/**
*A Class that contains information about each of the different preset
*controller types.
*@param {String} n The name of the controller type. This name is also used
*within the UI for the controller type dropdown menu.
*@param {Function} f The function to be run to aquire the proper expression
*string to be placed in the correct property.
*@returns {ControllerType} The ControllerType Object created with the given
*arguments.
*/
COB.CC.ControllerType = function (n, f)
{
    var name = n, func;

    func = (f) ? f : null;

    return {
        getName: function () {
            return name;
        },
        getFunc: function () {
            return func;
        },
        setFunc: function (f) {
            func = f;
        }
    };
};

/**
 *The different controller types that can be created.
 *@type {Array}
 */
COB.CC.controllerTypes = [
    COB.CC.ControllerType(
        "Individual Key To Key Slider",
        function (o) {
            var j, currentProperty, slider, possibleSliderName;
            for (j = 0; j < o.currentProperties.length; j += 1)
            {
                currentProperty = o.currentProperties[j];

                if (currentProperty.canSetExpression)
                {
                   slider = o.controller.property("Effects").
                        addProperty("ADBE Slider Control").property(1);

                    possibleSliderName = o.currentPropLayer.name +
                        "." + currentProperty.name;

                    while (possibleSliderName.length > 31 ||
                           possibleSliderName === null)
                    {
                        possibleSliderName = prompt("After Effects " +
                            "only allows 31 characters for effect " +
                            "names. The name \"" +
                            possibleSliderName + "\" is " +
                            "unfortunately too long. Please enter in " +
                            "a new name: ",
                        possibleSliderName.substr(0,31),
                        "Controller Name Too Long");
                    } //End while possibleSliderName is not bueno.

                    if (possibleSliderName !== null)
                    {
                        slider.parentProperty.name = possibleSliderName;

                       currentProperty.expression = "" +
                        "if (thisProperty.numKeys == 2) {" +

                            "controller = comp(\"" + o.receivingComp.name +
                                "\")" +
                            ".layer(\"" + o.controller.name + "\")" +
                            ".effect(\"" + possibleSliderName +
                                "\")('Slider');\n" +
                            "k1 = thisProperty.key(1).value; \n" +
                            "k2 = thisProperty.key(2).value; \n" +
                            "linear(controller, 0, 100, k1, k2);" +
                        "} else {\n" +
                        "value;\n" +
                        "}";
                    }//End if possibleSliderName is not null
                } //End if can set expression
            } //End current properties loop
        }),
    COB.CC.ControllerType(
        "Layer Opacity On/Off",
        function (o) {
            var j, currentProperty, slider, possibleCheckbox,
                atLeastOneProp = false;

            possibleCheckbox = o.currentPropComp.name + "-" +
                o.currentPropLayer.name + " on/off";

            while (possibleCheckbox.length > 31)
            {
                possibleCheckbox = prompt("After Effects " +
                    "only allows 31 characters for effect " +
                    "names. The name \"" +
                    possibleCheckbox + "\" is " +
                    "unfortunately too long. Please enter in " +
                    "a new name: ",
                possibleCheckbox.substr(0,31),
                "Controller Name Too Long");
            } //End while possibleSliderName is not bueno.

            if (possibleCheckbox !== null)
            {
                slider = o.controller.property("Effects").
                        addProperty("ADBE Checkbox Control").property(1);
                slider.parentProperty.name = possibleCheckbox;

                for (j = 0; j < o.currentProperties.length; j += 1)
                {
                    currentProperty = o.currentProperties[j];

                    if (currentProperty.canSetExpression &&
                        currentProperty.matchName === "ADBE Opacity")
                    {
                       currentProperty.expression = "" +
                            "controller = comp(\"" + o.receivingComp.name +
                                "\")" +
                            ".layer(\"" + o.controller.name + "\")" +
                            ".effect(\"" + possibleCheckbox +
                                "\")(\"Checkbox\");\n" +
                            "controller*100;";
                        atLeastOneProp = true;
                    } //End if can set expression
                } //End current properties loop

                if(slider.parentProperty.propertyType ===
                    PropertyType.INDEXED_GROUP && !atLeaseOneProp)
                {
                    //None of the selected properties could be set because
                    //their canSetExpression field is false. Delete the slider
                    //we just created.
                    slider.remove();
                }
            }//End if possibleSliderName is not null
        }),
        COB.CC.ControllerType(
        "Layer Key To Key Slider",
        function (o) {
            var j, currentProperty, slider, possibleSliderName,
                atLeastOneProp = false;

            possibleSliderName = o.currentPropComp.name + "-" +
                o.currentPropLayer.name;

            while (possibleSliderName.length > 31)
            {
                possibleSliderName = prompt("After Effects " +
                    "only allows 31 characters for effect " +
                    "names. The name \"" +
                    possibleSliderName + "\" is " +
                    "unfortunately too long. Please enter in " +
                    "a new name: ",
                possibleSliderName.substr(0,31),
                "Controller Name Too Long");
            } //End while possibleSliderName is not bueno.

            if (possibleSliderName !== null)
            {
                slider = o.controller.property("Effects").
                        addProperty("ADBE Slider Control").property(1);
                slider.parentProperty.name = possibleSliderName;

                for (j = 0; j < o.currentProperties.length; j += 1)
                {
                    currentProperty = o.currentProperties[j];

                    if (currentProperty.canSetExpression)
                    {
                       currentProperty.expression = "" +
                        "if (thisProperty.numKeys == 2) {" +

                            "controller = comp(\"" + o.receivingComp.name +
                                "\")" +
                            ".layer(\"" + o.controller.name + "\")" +
                            ".effect(\"" + possibleSliderName +
                                "\")(\"Slider\");\n" +
                            "k1 = thisProperty.key(1).value; \n" +
                            "k2 = thisProperty.key(2).value; \n" +
                            "linear(controller, 0, 100, k1, k2);" +
                        "} else {\n" +
                        "value;\n" +
                        "}";
                        atLeastOneProp = true;
                    } //End if can set expression
                } //End current properties loop

                if(slider.parentProperty.propertyType ===
                    PropertyType.INDEXED_GROUP && !atLeaseOneProp)
                {
                    //None of the selected properties could be set because
                    //their canSetExpression field is false. Delete the slider
                    //we just created.
                    slider.remove();
                }
            }//End if possibleSliderName is not null
        })
    ];

/**
 *Language strings used within this script
 */
COB.CC.lang = {
    //Individual words
    current: "Current",
    controller: "Controller",
    refresh: "\u27F2",
    selectCurrentSymbol: "\u27A1",
    go: "Go",

    //Phrases
    "controllerType": {en: "Controller Type:"},
    "receivingComposition": {en: "Recieving Composition:"},
    "newController": {en: "+"},
    "windowName": {en: "COB - Controller Creator"},
    "help": {en: "?"},
    "refreshCompList": {en: "Refresh List"},
    "selectComposition": {en: "Select a composition..."},
    "enterInControllerName": {en: "Set the controller's name:"},
    "defaultControllerName": {en: "Controller"},
    "newControllerPromptTitle": {en: "New Controller"}
};

/**
 *Initiates the UI
 *@param {Object} thisObj The Window or Palette "this" used to determine whether
 *or not this UI should be set up as a Window or Palette.
 *@returns {Object} The CC UI Object
 *@constructor
 *@see COB.CC
 *@extends COB.CC
 */
COB.CC.UI = (function (thisObj)
{
    COB.LOG.debug("Initializing CC UI");

    ////////////////////////////////////////////////////////////////////////////
    // Shortcut Variable Declarations
    ////////////////////////////////////////////////////////////////////////////

    var receivingCompRow,
    controllerRow,
    buttonRow,


    ////////////////////////////////////////////////////////////////////////////
    // Misc. Variables
    ////////////////////////////////////////////////////////////////////////////

    res,
    i, //For loops

    //Window/Panel setup
    myWindow = (thisObj instanceof Panel) ?
        thisObj :
        new Window("palette",
        COB.CC.scriptName + " " + COB.CC.scriptVersion,
        undefined,
        {
            "resizeable": false
        }),

    ////////////////////////////////////////////////////////////////////////////
    // Private method variable declarations
    ////////////////////////////////////////////////////////////////////////////

    display,
    getReceivingCompName,
    getControllerName,
    setReceivingCompList,
    setControllerList,
    setReceivingComp,
    setController,
    setReceivingCompRefreshHandler,
    setCurrentHandler,
    setControllerRefreshHandler,
    setGoHandler,
    setHelpHandler,
    refreshControllerList,

    unicodeGlyphFont,
    darkPen,
    iconButtonDemension,
    DDLItemDemension;

    iconButtonDemension = "[25,25]";
    DDLItemDemension = "[150, 25]";

    COB.LOG.debug("Trying to add UI Res");

    //Add the resources for this window
    try {
        res = "group {orientation: 'column', alignment: 'right', " +
                "margins: 10, spacing: 5," +
                "receivingCompRow: Panel {orientation: 'row', alignment: 'fill', "+
                    "text: '" +
                        localize(COB.CC.lang.receivingComposition) + "'," +
                    "currentButton: Button {text: '" +
                            localize(COB.CC.lang.selectCurrentSymbol) + "', " +
                        "preferredSize: " + iconButtonDemension + "}" +
                    "i: DropDownList {preferredSize: [150,20], " +
                        "itemSize: " + DDLItemDemension + "," +
                        "alignment: ['fill', 'middle']}, " +
                    "refreshButton: Button {text: '" + 
                        localize(COB.CC.lang.refresh) + "', " +
                        "preferredSize: " + iconButtonDemension + "}, " +
                "}," + //End receivingCompRow
                "controllerRow: Panel {orientation: 'row', alignment: 'fill', " +
                    "text: '" + localize(COB.CC.lang.controller) + "'," +
                    "alignment: ['right', 'middle'], " +
                    "currentButton: Button {text: '" +
                            localize(COB.CC.lang.selectCurrentSymbol) + "', " +
                        "preferredSize: " + iconButtonDemension + "}," +
                    "i: DropDownList {preferredSize: [150,20], " +
                        "itemSize: " + DDLItemDemension + "," +
                        "alignment: ['fill', 'middle']}, " +
                    "refreshButton: Button {text: '" +
                        localize(COB.CC.lang.refresh) + "', " +
                        "preferredSize: " + iconButtonDemension + "}, " +
                    "newButton: Button {text: '" +
                        localize(COB.CC.lang.newController) + "', " +
                        "preferredSize: " + iconButtonDemension + "}" +
                "}," + //End controllerRow
                "typeRow: Panel {orientation: 'row', " +
                    "alignment: ['fill', 'middle'], " +
                    "text: '" + localize(COB.CC.lang.controllerType) + "'," +
                    "i: DropDownList {alignment: ['fill', 'fill'] " +
                        "itemSize: " + DDLItemDemension + "," +
                    "}, " +
                "}," + //End controllerRow
                "buttonRow: Group {orientation: 'row', alignment: 'right', "+
                    "goButton: Button {text: '" +
                        localize(COB.CC.lang.go) + "'}" +
                    "helpButton: Button {text: '" +
                        localize(COB.CC.lang.help) + "' }" +
                "}," + //End buttonRow"
            "}"; //End main group
        //Build the window
        myWindow.grp = myWindow.add(res);

    } catch (e) {
        COB.UI_helper.resError(e, res);
    }

    //Have the layout calculate the position of its resources.
    myWindow.layout.layout(true);
    myWindow.onResizing = myWindow.onResize = function () {
        this.layout.resize();
    };

    //Setup event functions that are controlled by the UI

    COB.LOG.debug("Assigning shortcut variables");

    ////////////////////////////////////////////////////////////////////////////
    // Shortcut UI Variables
    ////////////////////////////////////////////////////////////////////////////

    receivingCompRow = myWindow.grp.receivingCompRow;
    controllerRow = myWindow.grp.controllerRow;
    typeRow = myWindow.grp.typeRow;
    buttonRow = myWindow.grp.buttonRow;

    ////////////////////////////////////////////////////////////////////////////
    // Setup Graphic Specifications
    ////////////////////////////////////////////////////////////////////////////

    darkPen = myWindow.graphics.newPen(
                myWindow.graphics.PenType.SOLID_COLOR,
                [0, 0, 0, 1], 1);

    receivingCompRow.i.graphics.foregroundColor = darkPen;
    controllerRow.i.graphics.foregroundColor = darkPen;
    typeRow.i.graphics.foregroundColor = darkPen;

    //FONT SPECIFICATION

    unicodeGlyphFont = ScriptUI.newFont(
        ScriptUI.applicationFonts.palette.name,
        ScriptUI.FontStyle.REGULAR,
        15
    );

    receivingCompRow.refreshButton.graphics.font = unicodeGlyphFont;
    receivingCompRow.currentButton.graphics.font = unicodeGlyphFont;
    controllerRow.refreshButton.graphics.font = unicodeGlyphFont;
    controllerRow.newButton.graphics.font = unicodeGlyphFont;



    ////////////////////////////////////////////////////////////////////////////
    // UI Values Setup
    ////////////////////////////////////////////////////////////////////////////

    //Set Controller Type list
    for (i = 0; i < COB.CC.controllerTypes.length; i += 1)
    {
        typeRow.i.add("item", COB.CC.controllerTypes[i].getName()).data = {
            controllerTypeIndex: i
        };
    }

    //Select the first item
    typeRow.i.items[0].selected = true;

    ////////////////////////////////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////////////////////////////////

     /**
     *Displays the window or palette. If it is a window, it is centered,
     *otherwise, the layout() function is called on the palette.
     *@returns Nothing.
     */
    display = function () {
        if (myWindow !== null)
        {
            if (myWindow instanceof Window)
            {
                myWindow.center();
                myWindow.show();
            } else {
                myWindow.layout.layout(true);
            }
        }
    };

    /**
     *Simulates an onClick event for the controller list
     *@return Nothing.
     */
    refreshControllerList = function () {
        controllerRow.refreshButton.notify();
    };
    // GETTERS

    /**
     *Returns the currently selected item's name in the receivingComp
     *dropdown menu
     *@returns {String|Null} Returns the text string belonging to the currently
     *selected item or null if nothing is selected.
     */
    getReceivingCompName = function () {
        COB.LOG.debug("Getting Receiving Comp Name");
        var s = receivingCompRow.i.selection;
        if (s !== null)
        {
            COB.LOG.debug("Receiving Comp Name: " + s.text);
            return s.text;
        } else {
            COB.LOG.debug("Returning Null");
            return null;
        }
        
    };

    /**
     *Gets the name of the currently selected controller type in the UI.
     *@returns {String|Bool} The text of the currently
     *selected control type
     */
    getSelectedControllerTypeName = function () {
        return typeRow.i.selection.text;
    };

    /**
     *Returns the currently selected Control item's name
     *@returns {String|Null} The name of the currently selected Controller or
     *null if there isn't anything selected in the Controller dropdown..
     */
    getControllerName = function () {
        COB.LOG.debug("Getting Controller Name");
        var s = controllerRow.i.selection;
        if (s !== null) {
            COB.LOG.debug("Controller Name: " + s.text);
            return s.text;
        } else {
            COB.LOG.debug("Returning Null");
            return null;
        }

    };

    // SETTERS

    /**
     *Populates the Receiving Comp List Box with the provided
     *array while keeping the currently selected items intact.
     *@param {Item[]} l An array of project Items with the type 'Footage' or
     *'Composition' to set as the list items in the dropdown menu.
     *@returns Nothing.
     */
    setReceivingCompList = function (l) {
        COB.LOG.debug("Setting Receiving Comp List");
        var prevSelSources, list, item, f;

        if (l && l.length !== 0)
        {
            prevSelSources = getReceivingCompName();
            list = receivingCompRow.i;
            f = setReceivingComp;

            //Remove all items from the lists
            list.removeAll();

            for (item = 0; item < l.length; item += 1)
            {
                    list.add("item", l[item].name);
            }

            //Set the previously selected sources
            if (prevSelSources !== null) {
                COB.LOG.debug("Selecting Previous Receiving Comp");
                f(prevSelSources);
            } else {
                list.items[0].selected = true;
                COB.LOG.debug("No item in list found...");
            }
            
            list.enabled = true;
        } else {
            COB.LOG.debug("No list provided");
            resetReceivingCompList();
        }
    };

    /**
     *Populates the Controller List Box with the provided
     *array while keeping the currently selected items intact. If no list is
     *sent, the controller list is reset.
     *@see COB.CC.UI.resetControllerList();
     *@param {String[]} [l] An array of Layer Name Strings. If not given,
     *the controller list is reset.
     *@param {Bool} [r] An optional parameter that, when no value is sent or
     *true is sent, the controller list will try to select the previously
     *selected controller. When false, the previous selection is not reselected.
     *@returns Nothing.f
     */
    setControllerList = function (l, r) {
        COB.LOG.debug("Setting Controller List");
        var prevSelSource, list, item, f;
        COB.LOG.debug("r SET TO: " + r);

        if (r === undefined)
        {
            r = true;
        }

        COB.LOG.debug("r SET TO: " + r);

        if (l && l.length !== 0)
        {
            prevSelSource = (r) ? getControllerName() : false;
            COB.LOG.debug("Previously Selected Source: " + prevSelSource);
            list = controllerRow.i;
            f = setController;

            //Remove all items from the lists
            list.removeAll();

            for (item = 0; item < l.length; item += 1)
            {
                COB.LOG.debug(l[item]);
                list.add("item", l[item]);
            }

            //Set the previously selected sources
            if (prevSelSource &&
                prevSelSource !== localize(COB.CC.lang.refreshCompList) &&
                prevSelSource !== null) {
                COB.LOG.debug("Selecting Previous Controller");
                f(prevSelSource);
            } else {
                COB.LOG.debug("First item in list selected");
                list.items[0].selected = true;
            }
            list.enabled = true;
        } else {
            COB.LOG.debug("No List Provided");
            resetControllerList();
        }
    };

    /**
     *Resets the controller list to have one item that is "--".
     */
    resetControllerList = function ()
    {
        COB.LOG.debug("Resetting Controller List");
        controllerRow.i.removeAll();
        controllerRow.i.add("item", localize(COB.CC.lang.chooseComp));
        controllerRow.i.enabled = false;
    };

    /**
     *Resets the receiving composition list.
     */
    resetReceivingCompList = function ()
    {
        COB.LOG.debug("Resetting Receiving Comp List");
        receivingCompRow.i.removeAll();
        receivingCompRow.i.add("item", localize(COB.CC.lang.chooseComp));
        receivingCompRow.i.enabled = false;
    };

    /**
     *Finds the first item in the receivingComp list that matches the name
     *provided.
     *@param {String} c The name of the item to choose in the receivingComp list
     *@returns {Bool} True if the receivingComp list was set successfully, false
     *if it wasn't.
     */
    setReceivingComp = function (c) {
        COB.LOG.debug("Setting Receiving Comp to: " + c);
        var i,
            theList = receivingCompRow.i.items,
            currentItem;

       for (i = 0; i < theList.length; i += 1)
       {
           currentItem = theList[i];
           if(currentItem.toString() === c) {
               COB.LOG.debug("Receiving Comp set!");
               currentItem.selected = true;
               return true;
           }
       }
       //The composition with the name 'c' could not be found
       COB.alertError(COB.CC.error.UNABLE_TO_SET_RECEIVING_COMP);
       return false;
    };

    /**
     *Finds the first item in the Controller list that matches the name
     *provided.
     *@param {String} c The name of the item to choose in the Controller list
     *@returns {Bool} True if the Controller list was set successfully
     */
    setController = function (c) {
        COB.LOG.debug("Setting Controller to " + c);
        var i,
            theList = controllerRow.i.items,
            currentItem;

       for (i = 0; i < theList.length; i += 1)
       {
           currentItem = theList[i];
           if(currentItem.toString() === c) {
               COB.LOG.debug("Current Item Set: " + currentItem.text);
               currentItem.selected = true;
               return true;
           }
       }
       //The composition with the name 'c' could not be found
       COB.alertError(COB.CC.error.UNABLE_TO_SET_CONTROLLER);
       return false;
    };

    //Event Handlers

    /**
     *Sets the onClick handler for the receivingComp refresh button
     *@param {Function} f The function to run onClick
     */
    setReceivingCompRefreshHandler = function (f) {
        COB.LOG.debug("Setting Receiving Comp Refresh Handler");
        receivingCompRow.refreshButton.onClick = f;
    };

    /**
     *Sets the onClick handler for the receivingComp current button
     *@param {Function} f The function to run onClick
     */
    setCurrentHandler = function (f) {
        COB.LOG.debug("Setting Current Handler");
        receivingCompRow.currentButton.onClick = f;
    };

    /**
     *Sets the onClick handler for the Controller refresh button
     *@param {Function} f The function to run onClick
     */
    setControllerRefreshHandler = function (f) {
        COB.LOG.debug("Setting Controller Refresh Handler");
        controllerRow.refreshButton.onClick = f;
    };

    setNewControllerHandler = function (f) {
        COB.LOG.debug("Setting New Controller Handler");
        controllerRow.newButton.onClick = f;
    };

    /**
     *Sets the onClick handler for the Go button
     *@param {Function} f The function to run onClick
     */
    setGoHandler = function (f) {
        COB.LOG.debug("Setting the Go Handler");
        buttonRow.goButton.onClick = f;
    };

    setHelpHandler = function (f) {
        COB.LOG.debug("Setting the Help Handler");
        buttonRow.helpButton.onClick = f;
    };

    setReceivingCompOnChange = function (f) {
        COB.LOG.debug("Setting the Receiving Comp On Change Handler");
        receivingCompRow.i.onChange = f;
    };

    ////////////////////////////////////////////////////////////////////////////
    // Public Methods
    ////////////////////////////////////////////////////////////////////////////

    //Make certain private methods public.

    return {
        display: display,
        refreshControllerList: refreshControllerList,

        // GETTERS
        
        getReceivingCompName: getReceivingCompName,
        getControllerName: getControllerName,
        getSelectedControllerTypeName: getSelectedControllerTypeName,

        // SETTERS

        setReceivingCompList: setReceivingCompList,
        setControllerList: setControllerList,
        setReceivingComp: setReceivingComp,
        setController: setController,
        resetControllerList: resetControllerList,
        setReceivingCompRefreshHandler: setReceivingCompRefreshHandler,
        setReceivingCompOnChange: setReceivingCompOnChange,
        setCurrentHandler: setCurrentHandler,
        setControllerRefreshHandler: setControllerRefreshHandler,
        setNewControllerHandler: setNewControllerHandler,
        setGoHandler: setGoHandler,
        setHelpHandler: setHelpHandler
    } //End Return
}(this)); // End COB.CC.UI



/*------------------------------------------------------------------\
    COB.CC METHODS
\------------------------------------------------------------------**/



COB.LOG.debug("Defining CC Methods");
/**
 *Displays help information to the user
 *@returns Nothing.
 */
COB.CC.help = function () {
    COB.LOG.debug("COB.CC.help called");
    alert("Help Documentation");
};

/**
*Gets a list of layers within the composition with the name provided
*for use in the controller dropdown menu.
*@param {String} compName The name of the composition to find within the current
*project's item list.
*@return {Layer[]|Bool|null} An array of Layers that are within the composition
*with the given name or false if a layer with the given name is undefined
*or null if no compName was provided.
*/
COB.CC.getControllerList = function (compName) {
    if (compName)
    {
        COB.LOG.debug("Getting Controller List");
        var v = COB.PROJ_helper.verifyProjAndActiveItem(), comp, contList;

        comp = COB.PROJ_helper.getCompByName(compName);
        if (v && comp)
        {
            contList = COB.COMP_helper.getFilteredLayers(comp, ['AVLayer',
                'ShapeLayer', 'FootageLayer']);
            COB.LOG.debug("Returning " + contList.length + "Layers");
            return contList;
        } else {
            return false;
        }
    } else {
        return null;
    }
    
};

/**
 *Grabs the currently selected controller type within the UI and returns the
 *associated ControlType.
 *@returns {COB.CC.ControlType} The ControlType that matches the currently
 *selected control type within the UI.
 */
COB.CC.getControllerType = function () {
    COB.LOG.debug("Getting controller type");
    var i,
        selectedControllerType = COB.CC.UI.getSelectedControllerTypeName(),
        l = COB.CC.controllerTypes.length;

    for (i = 0; i < l; i += 1)
    {
        if (COB.CC.controllerTypes[i].getName() === selectedControllerType)
        {
            return COB.CC.controllerTypes[i];
        }
    }
    return false;
}

/**
 *Grabs the current collection of CompItems within the project.
 *@return {CompItem[]|Bool} An array of CompItems within the project or False
 *if none could be found.
 */
COB.CC.getCompList = function () {
    COB.LOG.debug("Getting Comp List");
    var v = COB.PROJ_helper.verifyProjAndActiveItem(), compList;
    if (v)
    {
        compList = COB.PROJ_helper.getCompList();
        COB.LOG.debug("Project Defined. Returning " + compList.length +
            "Compositions");
        return compList;
    } else {
        COB.LOG.debug("No Comps returned");
        return false;
    }
    
};

/**
 *Get the currently active Composition within the project. If a composition is
 *not the currently active item, null is returned.
 *@returns {CompItem|Null} The currently selected CompItem is returned or, if
 *the current active item is null or not a CompItem, null is returned.
 */
COB.CC.getCurrentCompName = function () {
    COB.LOG.debug("Getting the current Composition name...")
    var v = COB.PROJ_helper.verifyProjAndActiveItem();
    if (v && v[1] instanceof CompItem) {
        COB.LOG.debug("Returning " + v[1].name);
        return v[1].name;
    } else {
        COB.LOG.debug(COB.CC.error.ACTIVE_ITEM_NOT_COMP);
        return null;
    }
};

/**
 *Creates a new null layer that is named through user input.
 *@returns {Bool} True if the controller was created successfully, false if it
 *wasn't created successfully.
 */
COB.CC.newController = function () {
    COB.LOG.debug("Creating new controller");
    var rcName = COB.CC.UI.getReceivingCompName(),
        receivingComp,
        controllerName,
        theController,
        v;
    COB.LOG.debug("Receiving Comp Name: " + rcName);
    //Make sure at least a project is defined
    v = COB.PROJ_helper.verifyProjAndActiveItem();

    if (v && v[0])
    {
        COB.LOG.debug("Prompting User for Controller Name");
        controllerName = prompt(
            localize(COB.CC.lang.enterInControllerName),
            localize(COB.CC.lang.defaultControllerName),
            localize(COB.CC.lang.newControllerPromptTitle));
        COB.LOG.debug("Controller Name: " + controllerName);
        //Determine if there is a valid receiving comp.
        if(rcName !== localize(COB.CC.lang.selectComposition) && controllerName)
        {
            receivingComp = COB.PROJ_helper.getCompByName(rcName);
            COB.LOG.debug("Receiving Comp: " + receivingComp.name);
            theController = receivingComp.layers.addNull();
            COB.LOG.debug("The Controller name: " + theController.name);
            theController.name = controllerName;
            COB.LOG.debug("Renamed to" + theController.name);
            //Refresh the controller list
            COB.LOG.debug("Refreshing Controller List");
            COB.CC.UI.refreshControllerList();
            COB.CC.UI.setController(theController.name);
            return true;
        } else {
            COB.alertError(COB.CC.error.NEED_TO_SELECT_RECEIVING_COMP);
            return false;
        }
    } else {
        COB.alertError(COB.ERROR.NO_OPEN_PROJECT);
        return false;
    }
}

COB.CC.createControllers = function () {
    var receivingComp,
        controller,
        controllerType = COB.CC.getControllerType(),
        activeComp,
        currentProperty,
        currentPropComp,
        currentPropLayer,
        slider,
        v, i, j;
    v = COB.PROJ_helper.verifyProjAndActiveItem();
    if (v && v[1])
    {
        if (v[1] instanceof CompItem)
        {
            receivingComp = COB.PROJ_helper.getCompByName(
                COB.CC.UI.getReceivingCompName()
            );
            controller = receivingComp.layers.byName(
                COB.CC.UI.getControllerName()
            );
            activeComp = v[1];
            //COB.LOG.debug("Active Item: " + activeComp.name);
            props = COB.COMP_helper.getAllSelectedProperties(activeComp);
            //COB.LOG.debug("Selected Properties: " + props.toSource());

            if (props)
            {
                COB.LOG.debug("Creating Controllers");
                app.beginUndoGroup("Create Controllers");
                for (i = 0; i < props.length; i += 1)
                {
                    currentPropComp = props[i].parent;
                    //COB.LOG.debug("Current Prop Comp: " + currentPropComp.name);
                    currentPropLayer = props[i].layer;
                    //COB.LOG.debug("Current Prop Layer: " + currentPropLayer.name);
                    currentProperties = props[i].selectedProperties;
                    //COB.LOG.debug("Current Properties: " + currentProperties.toSource());

                    //Run the function associated with the ControlType
                    controllerType.getFunc()({
                        "currentPropComp": currentPropComp,
                        "currentPropLayer": currentPropLayer,
                        "currentProperties": currentProperties,
                        "receivingComp": receivingComp,
                        "controller": controller
                    });
                }
                app.endUndoGroup();
                return true;
            } else {
                COB.alertError(COB.CC.error.NO_SELECTED_PROPERTIES);
                return false;
            }

        } else {
            COB.alertError(COB.CC.error.ACTIVE_ITEM_NOT_COMP);
            return false;
        }
    } else {
        COB.alertError(COB.CC.error.ACTIVE_ITEM_NOT_FOUND);
        return false;
    }

};
/*------------------------------------------------------------------\
    COB.CC MAIN
\------------------------------------------------------------------**/

COB.CC.UI.setControllerRefreshHandler(
    function () {
        COB.CC.UI.setControllerList(
            COB.CC.getControllerList(
                COB.CC.UI.getReceivingCompName()
            )
        );
    }
);

COB.CC.UI.setCurrentHandler (
    function () {
        var n = COB.CC.getCurrentCompName();
        if (n) {
            COB.CC.UI.setReceivingComp(n);
        } else {
            alert(COB.CC.error.ACTIVE_ITEM_NOT_COMP);
        }
    }
);

COB.CC.UI.setGoHandler(
    function () {
        COB.CC.createControllers(
            COB.CC.UI.getReceivingCompName,
            COB.CC.UI.getControllerName
        );
    }
);

COB.CC.UI.setReceivingCompRefreshHandler(function () {
    COB.CC.UI.setReceivingCompList(COB.CC.getCompList());
    COB.CC.UI.setControllerList(
        COB.CC.getControllerList(
            COB.CC.UI.getReceivingCompName()
        ), false
    );
});

COB.CC.UI.setNewControllerHandler(function () {
    COB.CC.newController();
});

COB.CC.UI.setHelpHandler(function () {
   COB.CC.help();
});

COB.CC.UI.setReceivingCompOnChange(function () {
    COB.CC.UI.setControllerList(
        COB.CC.getControllerList(
            COB.CC.UI.getReceivingCompName()
        ), false
    );
});

//Populate the drop down menus
//Receiving Comp List
COB.CC.UI.setReceivingCompList(COB.CC.getCompList());

//Controller List
COB.CC.UI.setControllerList(
    COB.CC.getControllerList(
        COB.CC.UI.getReceivingCompName()
    ), false
);

//Write the script start time
COB.LOG.insertLogStart(COB.CC.scriptName);

//Setup UI event handlers

//Display the UI
COB.CC.UI.display();
